package edu.hawaii.myisern.sanitycheck;

import java.io.IOException;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.xml.sax.SAXException;

import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebResponse;

import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.collaborations.jaxb.Collaborations;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.organizations.jaxb.Organizations;
import edu.hawaii.myisern.researchers.jaxb.Researcher;
import edu.hawaii.myisern.researchers.jaxb.Researchers;
/**
 * Generates a report so as to fix invalid data.
 * @author John Hauage
 *
 */
public class SanityCheck {
	private List<SanityCheckErrorItem> report;
	private Collaborations collaborations;
	private Organizations organizations;
	private Researchers researchers;
	private static String ERROR = "error";
	private static String VALID = "valid";
	private static String string = " String.";
	private static Logger logger = Logger.getLogger("edu.hawaii.myisern.Hauge");
	/**
	 * Constructor.
	 * @param collaborations Collaborations.
	 * @param organizations Organizations.
	 * @param researchers Researchers.
	 * @throws Exception if error occurs.
	 */
	public SanityCheck(Collaborations collaborations, 
	    Organizations organizations,
	    Researchers researchers) throws Exception {
	  this.collaborations = collaborations;
	  this.organizations = organizations;
		this.researchers = researchers;
		this.report = new ArrayList<SanityCheckErrorItem>();
		//verifyResearchers();
	}
	/**
   * Verifies all collaboration.
   * @throws Exception if error occurs.
   */
  public void verifyCollaborations() throws Exception {
    for (Collaboration collab : this.collaborations.getCollaboration()) {
      this.report.add(verifyCollaboration(collab));
    }
  }
	/**
	   * Checks the validation of a supplied collaboration object.
	   * 
	   * @param collab Collaboration to validate.
	   * @return True if valid, False if not valid.
	   * @throws Exception Handle thrown exceptions.
	   */
	
	  public SanityCheckErrorItem verifyCollaboration(Collaboration collab) throws Exception {
	    SanityCheckErrorMessage errorMsg;
	    SanityCheckErrorItem errorItem;
	    try {
	      List<String> collabOrgList = collab.getCollaboratingOrganizations()
	          .getCollaboratingOrganization();
	      List<String> collabTypeList = collab.getCollaborationTypes().getCollaborationType();
	      List<String> outcomeTypeList = collab.getOutcomeTypes().getOutcomeType();
	      //System.out.println(collab.getName());
	      // valid name
	      	// valid string
	      errorMsg = isValidString(collab.getName());
        errorItem = new SanityCheckErrorItem(collab, collab.getName());
        if (!errorMsg.isValid()) {
          errorItem.addError("Name: " + 
                             errorMsg.getMessage() + ".");
        }
        // valid Collaborating-Organizations
	      	// valid string
	      	// minimum amount of two
	      // valid Collaboration-Type
	      	// valid string
	      	// minimum amount of one
	      // valid Collaboration-Type
	      	// valid string
	      	// minimum amount of one
	      // valid Year
	      	// between VALID_MIN_YEAR and VALID_MAX_YEAR
	      // valid Outcome-Type
	      	// valid string
	      	// minimum amount of one
	      // valid Description
	      	// valid string
        errorMsg = isValidString(collab.getDescription());
        //errorItem = new SanityCheckErrorItem(collab, collab.getDescription());
        if (!errorMsg.isValid()) {
          errorItem.addError("Description: " + 
                             errorMsg.getMessage() + ".");
        }
        return errorItem;
	    }
	    catch (Exception e) {
	      logger.log(Level.SEVERE, e.toString());
        return null;
	    }
	  }
	  
	  /**
	   * Verifies all organization.
	   * @throws Exception if error occurs.
	   */
	  public void verifyOrganizations() throws Exception {
	    for (Organization org : this.organizations.getOrganization()) {
	      this.report.add(verifyOrganization(org));
	    }
	  }
	  /**
	   * Validates an organization.
	   * 
	   * @param org Organization to validate.
	   * @return True if organization is valid, False if not.
	   * @throws Exception Handle thrown exceptions.
	   */
	
	  private SanityCheckErrorItem verifyOrganization(Organization org) throws Exception {
	    SanityCheckErrorMessage errorMsg;
      SanityCheckErrorItem errorItem;
      try {
	      List<String> affilateResearcherList = org.getAffiliatedResearchers()
	          .getAffiliatedResearcher();
	      List<String> researchKeywordList = org.getResearchKeywords().getResearchKeyword();
	      //System.out.println(org.getName());
	      // valid name
	        // valid string
	      errorMsg = isValidString(org.getName());
	      errorItem = new SanityCheckErrorItem(org, org.getName());
	      if (!errorMsg.isValid()) {
	        errorItem.addError("Name: " + 
	                           errorMsg.getMessage() + ".");
	      }
	      // valid type
	        // valid string
	      errorMsg = isValidString(org.getType());
        //errorItem = new SanityCheckErrorItem(org, org.getType());
        if (!errorMsg.isValid()) {
          errorItem.addError("Type: " + 
                             errorMsg.getMessage() + ".");
        }
	      // valid contact
	        // valid string
        errorMsg = isValidString(org.getType());
        //errorItem = new SanityCheckErrorItem(org, org.getContact());
        if (!errorMsg.isValid()) {
          errorItem.addError("Contact: " + 
                             errorMsg.getMessage() + ".");
        }
        // valid affiliated-researcher
	        // valid string
        // valid country
	        // valid string
        errorMsg = isValidString(org.getCountry());
        //errorItem = new SanityCheckErrorItem(org, org.getCountry());
        if (!errorMsg.isValid()) {
          errorItem.addError("Country: " + 
                             errorMsg.getMessage() + ".");
        }
        // valid research-keyword
	        // valid string
	        // minimum amount of one
	      // valid research-description
	        // valid string
        errorMsg = isValidString(org.getResearchDescription());
        //errorItem = new SanityCheckErrorItem(org, org.getResearchDescription());
        if (!errorMsg.isValid()) {
          errorItem.addError("Description: " + 
                             errorMsg.getMessage() + ".");
        }
        // valid home-page
	        // valid web link
	      // isValidWebLink(org.getHomePage())
        //System.out.println("ErrorItem: " + errorItem.getName());
	      return errorItem;
	    }
	    catch (Exception e) {
	      logger.log(Level.SEVERE, e.toString());
        return null;
	    }
	  }
	  
   /**
    * Returns the validation report;
    * @return The Report.
    */
  public List<SanityCheckErrorItem> getReport() {
		return this.report;
	}
  /**
   * Verifies all researchers.
   * @throws Exception if error occurs.
   */
  public void verifyResearchers() throws Exception {
    for (Researcher researcher : this.researchers.getResearcher()) {
      this.report.add(verifyResearcher(researcher));
    }
  }
   /**
    * Validates a researcher.
    * 
    * @param researcher Researcher to validate.
    * @return True if researcher is valid, False if not valid.
    * @throws Exception Handle thrown exceptions.
    */
   public SanityCheckErrorItem verifyResearcher(Researcher researcher) throws Exception {
	 SanityCheckErrorMessage errorMsg;
	 SanityCheckErrorItem errorItem;
     try {
       // valid name
         // valid string
       errorMsg = isValidString(researcher.getName());
       errorItem = new SanityCheckErrorItem(researcher, researcher.getName());
       if (!errorMsg.isValid()) {
         errorItem.addError("Name: " + 
                            errorMsg.getMessage() + ".");
       }
       // valid organization
        // valid string
       /*
       errorItem.addError(researcher.getOrganization()  + ": " + 
                          "Organization un-verified.");
       */
       // valid email
         // email formated string
       errorMsg = isValidEmail(researcher.getEmail());
       if (!errorMsg.isValid()) {
         errorItem.addError("Email: " + 
                            errorMsg.getMessage() + ".");
       }
       // valid picture-link
       	 // valid web link
       //errorItem.addError("Picture-Link un-verified.");
       // valid bio-statement
         // valid string
  	   errorMsg = isValidString(researcher.getBioStatement());
  	   if (!errorMsg.isValid()) {
  	     errorItem.addError("Bio-Statement: " + 
                            errorMsg.getMessage() + ".");
       }
       return errorItem;
     }
     catch (Exception e) {
       logger.log(Level.SEVERE, e.toString());
       return null;
     }
   }
	  /**
	   * Validates a string.
	   * 
	   * @param s String to validate.
	   * @return True if string is valid, False if not valid.
	   */
	  private static SanityCheckErrorMessage isValidString(String s) {
		String msg = "";
		boolean pass = true;
	    try {
	      // not ""
	      if ("".equals(s)) {
	    	msg = "blank";
	        pass = false;
	      }

	      // not null
	      else if (s == null) {
	    	msg = "null";
	        pass = false;
	      }
	      else {
	    	msg = VALID;
	        pass = true;
	      }
	      return new SanityCheckErrorMessage(msg,pass);
	    }
	    catch (Exception e) {
	      logger.log(Level.SEVERE, e.toString());
        return null;
	    }
	  }

	  /**
	   * Validates the year according to the VALID_MIN_YEAR & VALID_MAX_YEAR values.
	   * 
	   * @param year Year to validate.
	   * @return True if year is valid, False if not valid.
	   * @throws Exception Handle thrown exceptions.
	   */
	  public static SanityCheckErrorMessage isValidYear(BigInteger year) throws Exception {
		String msg = "";
	    boolean pass = true;
	    try {
	      int curYear = year.intValue();
	      // then check most recent year is between min year && max year
	      if (1990 <= curYear && curYear <= 2010) {
	        // year is valid and we can attempt to add the unique id to the list
	    	msg = VALID;
	        pass = true;
	      }
	      else {
	    	msg = "year not between 1990 && 2010";
	        pass = false;
	      }
	      return new SanityCheckErrorMessage(msg, pass);
	    }
	    catch (Exception e) {
	      logger.log(Level.SEVERE, e.toString());
        return null;
		}
	  }
	  /**
	   * Validates an email address.
	   * 
	   * @param email Email address to validate.
	   * @return True if email is valid, False if not valid.
	   * @throws Exception Handle thrown exceptions.
	   */
	  private SanityCheckErrorMessage isValidEmail(String email) throws Exception {
	    /*
	     * Code borrowed and inspired by Sun Corporation
	     * http://java.sun.com/developer/technicalArticles/releases/1.4regex/index.html
	     */
		String msg = "";
	    boolean pass = true;
	    try {
	      // Checks for empty address
        if (email == null || email.isEmpty()) {
          msg = "empty\\null";
          pass = false;
        }
        else {
  	      // Checks for email addresses starting with
  	      // inappropriate symbols like dots or @ signs.
  	      Pattern p = Pattern.compile("^\\.|^\\@");
  	      Matcher m = p.matcher(email);
  	      if (m.find()) {
  	        msg = "starts with an inappropriate symbols like dots or @ signs";
  	        pass = false;
  	      } 
  	      else {
    	      // Checks for email addresses that start with
    	      // www. and prints a message if it does.
    	      p = Pattern.compile("^www\\.");
    	      m = p.matcher(email);
    	      if (m.find()) {
    	        msg = "starts with www";
    	        pass = false;
    	      }
    	      else {
      	      // Check for email to be in [name]@[domain] format
      	      // '\\w' == [a-zA-Z0-9]
      	      p = Pattern.compile("^[\\w|\\.]+@[\\w|\\.]+");
      	      m = p.matcher(email);
      	      if (m.find()) {
      	        msg = VALID;
      	        pass = true;
      	      }
      	      else {
                msg = "not in [name]@[domain] format";
                pass = false;
              }
    	      }
  	      }
        }
	      return new SanityCheckErrorMessage(msg, pass);
	    }
	    catch (Exception e) {
	      logger.log(Level.SEVERE, e.toString());
	      return null;
	    }
	  }

	  /**
	   * Checks validation of web links..
	   * 
	   * @param url URL to test.
	   * @throws SAXException Thrown exception.
	   * @throws IOException Thrown exception.
	   * @throws MalformedURLException Thrown exception.
	   * @return if valid or not.
	   */
	  public SanityCheckErrorMessage isValidWebLink(String url) throws MalformedURLException, 
	                                                   IOException, 
	                                                   SAXException {
	    try {
	      WebConversation wc = new WebConversation();
	      WebResponse response = wc.getResponse(url);
	      return new SanityCheckErrorMessage(VALID, true);
	    }
	    catch (UnknownHostException e) {
	      SanityCheckErrorMessage scem = new SanityCheckErrorMessage(ERROR, false);
  		  scem.setErrorMessage(e.getMessage());
  		  return scem;
	    }
	    catch (Exception e) {
	      SanityCheckErrorMessage scem = new SanityCheckErrorMessage(ERROR, false);
  		  scem.setErrorMessage(e.getMessage());
  		  return scem;
	    }

	  }
}
